from __future__ import division
"""

 backend_lablplot.py

 The name of the backend must start with "WX"
 in order to be compatible with ipython -pylab option

 A wxPython backend for UNIMES with Agg.  This uses the GUI widgets written by
 Jeremy O'Donoghue (jeremy@o-donoghue.com) and the Agg backend by John
 Hunter (jdhunter@ace.bsd.uchicago.edu)

 Copyright (C) 2008 Chaffra Awo-Affouda, Naval Research Laboratory, Washington, D.C.


 License:

"""

#import sys, os, os.path, math, StringIO

#import matplotlib as mpl
#matplotlib.rcsetup.interactive_bk.append('iplot.mpl.backend_iplot')
import matplotlib.backends
from matplotlib._pylab_helpers import Gcf
from matplotlib.figure import Figure
#from matplotlib import verbose, rcParams
#from matplotlib.rcsetup import all_backends, _validate_standard_backends, ValidateInStrings
#from matplotlib.backend_bases import NavigationToolbar2 #RendererBase, GraphicsContextBase,\
#     FigureCanvasBase, FigureManagerBase, , \
#     cursors

#try:
#import wx
import wx.aui

#import wx.py as py
backend_version = wx.VERSION_STRING
    #from wx import ScrolledWindow

#import matplotlib.backends.backend_wx
from matplotlib.backends.backend_wx import FigureManagerWx,\
        StatusBarWx, draw_if_interactive, error_msg_wx, DEBUG_MSG

    #from backend_wx import *

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg


#except:
#    print >>sys.stderr, "Matplotlib backend_iplot requires backend_wxagg be installed"
#    sys.exit()

#import iplot
from iplot.mpl.wx_menubar import MenubarIplot
from iplot.mpl.wx_toolbar import IPlotToolbar
#from iplot.mpl.wx_widgets import *
from iplot.mpl.wx_dialog import PlotMixDialog, DataDialog, PlotfileDialog, PropertyDialog

#from iplot.mpl.debug import DEBUG_MSG

matplotlib.backends.backend_wx._DEBUG = 5
#iplot.mpl.debug._DEBUG = _DEBUG


from iplot.mpl.wx_utils import bind
            
class IPlotFrame(wx.Frame):

    def __init__(self,size=wx.DefaultSize):
        # On non-Windows platform, explicitly set the position - fix
        # positioning bug on some Linux platforms
        if wx.Platform == '__WXMSW__':
            pos = wx.DefaultPosition
        else:
            pos =wx.Point(100,100)
        #l,b,w,h = fig.bbox.bounds

        wx.Frame.__init__(self, parent=None, id=-1, pos=pos,
                          title="iPlot",
                          size=size
                          )
        DEBUG_MSG("__init__()", 1, self)
        #self.num = num

        self.menubar = self._get_menubar()
        self.SetMenuBar(self.menubar)
        
        self.mixdlg = PlotMixDialog(self, -1, "Plot Mix...",
                         #style=wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME,
                         style=wx.DEFAULT_DIALOG_STYLE, # & ~wx.CLOSE_BOX,
                         useMetal=False
                         )
        self.mixdlg.CenterOnScreen()
        
        self.datadlg = DataDialog(self, -1, "Show Data...",
                         #style=wx.CAPTION | wx.SYSTEM_MENU | wx.THICK_FRAME,
                         style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, # & ~wx.CLOSE_BOX,
                         useMetal=False
                         )
        
        self.plotfiledlg = PlotfileDialog(self,-1,"Plot file")
        
        self.propertydlg = PropertyDialog(self,-1,"Edit Figure ...")

        

        #self.canvas = self.get_canvas(self.splitwin,fig)
        
        
        #self.SetSizeWH(_W,size.GetHeight())
        #self.Layout()
        
        self.plotbook = PlotBook(self)
        
        self.databook = DataBook(self)
        self.databook.SetPosition(wx.Point(300,300))
        

        self.auimgr = wx.aui.AuiManager(self)
        #self.sizer = wx.BoxSizer(wx.VERTICAL)
        
        
        pb_info = wx.aui.AuiPaneInfo()
        pb_info.Floatable(False)
        pb_info.CloseButton(False)
        pb_info.CenterPane()
        
        self.auimgr.AddPane(self.plotbook,pb_info)
        
        db_info = wx.aui.AuiPaneInfo()
        db_info.Floatable(True)
        db_info.CloseButton(False)
        db_info.Float()
        db_info.Hide()
        db_info.FloatingPosition(wx.Point(300,300))
        db_info.FloatingSize(wx.Size(300,300))
        
        self.auimgr.AddPane(self.databook,db_info)
        
        self.auimgr.Update()
        
        self._get_shell()

        self.statbar = StatusBarWx(self)
        self.SetStatusBar(self.statbar)
        self.toolbar = None

        bind(self,wx.EVT_CLOSE,self._onClose)
        DEBUG_MSG("bind()", 1, self)
        
        #self.FitInside()


    def _onClose(self, evt):
        DEBUG_MSG("onClose()", 1, self)
        #self.plotbook.OnClose(evt)
        #wxapp = wx.GetApp()
        #wxapp.iplot = None
        #self.ipycrust.Destroy()
        #self.canvas.stop_event_loop()
        #Gcf.destroy(self.num)
        #self.Destroy()
        self.Hide()

    #def get_canvas(self,parent,fig):
    #    return FigureCanvasIPlot(parent, -1, fig)
    
    def _get_shell(self):

        try:
            __IPYTHON__
        except NameError:

            from IPython.gui.wx.ipython_view import IPShellWidget
            import wx.stc
            _shell = IPShellWidget(self)
            #_shell.threading_option.SetValue(True)
            #_shell.threading_option.Disable()
            
            _shell.text_ctrl.SetWrapMode(wx.stc.STC_WRAP_CHAR)
            
            sh_info = wx.aui.AuiPaneInfo()
            sh_info.CloseButton(False)
            sh_info.Right()
            #sh_info.MinSize((400,400))
            
            self.auimgr.AddPane(_shell,sh_info,caption='Python shell')
            self.auimgr.Update()
            
            o = _shell.getOptions()
            o['threading']['value'] = 'False'
            _shell.reloadOptions(o)
            _shell.threading_option.Hide()
            #self.auimgr.Update()
            return _shell
            # Now ipshell() will open IPython anywhere in the code
        else:
            # Define a dummy ipshell() so the same code doesn't crash inside an
            # interactive IPython
            def ipshell(): pass
            return None

    def _get_menubar(self):
        return MenubarIplot(self)
    
    def GetToolBar(self):
        """Override wxFrame::GetToolBar as we don't have managed toolbar"""
        return self.toolbar

    def GetMenuBar(self):
        """
            Override wxFrame::GetMenuBar as we don't have managed menubar
        """
        return self.menubar

    def Destroy(self, *args, **kwargs):
        #DEBUG_MSG("Destroy()", 1, self)
        
        wx.Frame.Destroy(self, *args, **kwargs)
        #if self.canvas is not None:
            #print 'canvas still running!'
            #self.canvas.Destroy()
        if self.toolbar is not None:
            self.toolbar.Destroy()
     
        wxapp = wx.GetApp()
        if wxapp:
            wxapp.Yield()
        return True
    
    def GetBestSize(self):
        pb_w,pb_h = self.plotbook.GetClientSize()
        #db_w,db_h = self.databook.GetSize()
        print "Canvas size: ",pb_w,',',pb_h
       
        #self.nb.SetSize(wx.Size(w,h))
        #self.nb.Fit()
        #self.nb.FitInside()
        #self.Fit()
        #self.FitInside()
       
       
        tw,th = self.toolbar.GetSize()
        bw,bh = self.statbar.GetSize()
       
        _W = max(pb_w,tw,bw)
        _H = pb_h + th + bh + self.plotbook.GetTabCtrlHeight()/2
        return wx.Size(_W,_H)
        

class IPlotFigureManager(FigureManagerWx):
    
    def destroy(self, *args):
        DEBUG_MSG("destroy()", 1, self)
        
        #self.frame.Destroy()
        #if self.tb is not None: self.tb.Destroy()
        #wx.GetApp().ProcessIdle()
        wx.WakeUpIdle()
        DEBUG_MSG("OnClose Successful()", 1, self)
        
    def resize(self, width, height):
        'Set the canvas size in pixels'
        self.canvas.SetInitialSize(wx.Size(width, height))
        self.window.GetSizer().Fit(self.window)
        #self.window.iplot.plotbook.SetClientSize(wx.Size(width, height))
        #self.window.iplot.SetSize(self.window.iplot.GetBestSize())
        #self.window.iplot.GetSizer().Layout()
        
        tw,th = self.window.iplot.toolbar.GetClientSize()
        bw,bh = self.window.iplot.statbar.GetSize()
        
        _W = width
        
        _H = height + bh
        
        self.window.iplot.SetClientSize(wx.Size(_W,_H))
        

class FigureCanvasIPlot(FigureCanvasWxAgg):
    
    filetypes = FigureCanvasWxAgg.filetypes.copy()
    filetypes['xml'] = 'Matplotlib XML file'
    
    def print_xml(self,*args,**kwargs):
        from iplot.mpl.backend_xml import FigureCanvasXml # lazy import
        xml = self.switch_backends(FigureCanvasXml)
        return xml.print_xml(*args, **kwargs)
        
        
#        filename = args[0]
#        if filename is None:
#            return
#        from iplot.mpl.fig2xml import Figure2XML
#        _xmlfig = Figure2XML(self.figure,'figure')
#        _xmlfig.print_xml(filename)
        
        
#    def _onSize(self, evt):
#        fig = self.GetParent()
#        #plotbook = fig.iplot.plotbook
#        
#        #auimgr = plotbook.GetAuiManager()
#        #pane = auimgr.GetPane(fig)
#        #print fig.num, ':' ,fig.IsShown()
#        
#        if not fig.IsShown():
#            return
#        else:
#            FigureCanvasWxAgg._onSize(self,evt)
        
            
#    def _onPaint(self, evt):
#        fig = self.GetParent()
#        if not fig.IsShown():
#            return
#        else:
#            self._onSize(evt)
#            FigureCanvasWxAgg._onPaint(self,evt)
#        
        
        
class FigureFrameIPlot(wx.Panel):
    def __init__(self, parent, num, fig, iplot, id = -1, **kwargs):
        wx.Panel.__init__(self, parent, id=id, **kwargs)
        self.figure = fig
        self.num = num
        self.canvas = FigureCanvasIPlot(self, -1, fig)
        
        self.canvas.SetInitialSize(wx.Size(fig.bbox.width, fig.bbox.height))
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.canvas,1,wx.EXPAND)

        self.iplot = iplot
        if iplot.toolbar is None:
            self.toolbar = self._get_toolbar(self.iplot.statbar)
            self.toolbar.Realize()
            iplot.toolbar = self.toolbar
            
            tb_info = wx.aui.AuiPaneInfo()
            tb_info.Floatable(False)
            tb_info.CloseButton(False)
            tb_info.Bottom()
            tb_info.CaptionVisible(False)
            tb_info.Resizable(False)
            tb_info.PaneBorder(False)
            
            iplot.auimgr.AddPane(self.toolbar,tb_info)
            iplot.auimgr.Update()
            
            if wx.Platform == '__WXMAC__':
                # Mac platform (OSX 10.3, MacPython) does not seem to cope with
                # having a toolbar in a sizer. This work-around gets the buttons
                # back, but at the expense of having the toolbar at the top
                #iplot.SetToolBar(self.toolbar)
                pass
            else:
                # On Windows platform, default window size is incorrect, so set
                # toolbar width to figure width.
                tw, th = self.toolbar.GetSizeTuple()
                fw, fh = self.canvas.GetSizeTuple()
                # By adding toolbar in sizer, we are able to put it at the bottom
                # of the frame - so appearance is closer to GTK version.
                # As noted above, doesn't work for Mac.
                self.toolbar.SetSize(wx.Size(fw, th))
                #sizer.Add(self.toolbar, 0)
                
                self.iplot.SetToolBar(self.toolbar)

        else:
            self.toolbar = iplot.toolbar
        
        sizer.Layout()
        self.SetSizer(sizer)
        #self.SetSize(sizer.GetSize())
        self.Fit()
        self.figmgr = IPlotFigureManager(self.canvas, num, self)
        
        
    def get_figure_manager(self):
        DEBUG_MSG("get_figure_manager()", 1, self)
        return self.figmgr
    
    def _onClose(self, evt):
        DEBUG_MSG("_onClose()", 1, self)
        
    def _get_toolbar(self, statbar):
        if matplotlib.rcParams['toolbar']=='toolbar2':
            toolbar = IPlotToolbar(self.iplot,self.canvas)
            toolbar.set_status_bar(statbar)
        else:
            toolbar = None
        return toolbar
    
    def GetToolBar(self):
        """Override wxFrame::GetToolBar as we don't have managed toolbar"""
        return self.toolbar
    
    def Destroy(self, *args, **kwargs):
        wx.Panel.Destroy(self, *args, **kwargs)
        #if self.toolbar is not None:
        #    self.toolbar.Destroy()
        #wxapp = wx.GetApp()
        #if wxapp:
        #    wxapp.Yield()
        return True
    
    
class DataBook(wx.aui.AuiNotebook):
    def __init__(self, parent, id = -1,size = wx.DefaultSize,pos=wx.Point(200,200)):
        wx.aui.AuiNotebook.__init__(self,parent,id=id,size=size,pos=pos)
        
        self.iplot = parent
        
        bind(self,wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE,self._onPageClose)
        bind(self,wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,self._onPageChanged)
        
    def _onPageClose(self, evt):
        DEBUG_MSG("_onPageClose()", 1, self)
        #num = self.GetSelection()
        #print num
        #page = self.GetPage(num)
        print self.GetPageCount()
        if self.GetPageCount() < 2:
            db_info = self.iplot.auimgr.GetPane(self)
            db_info.Hide()
            self.iplot.auimgr.Update()
                
    def _onPageChanged(self,evt):
        DEBUG_MSG("_onPageChanged()", 1, self)
        db_info = self.iplot.auimgr.GetPane(self)
        db_info.Show()
        self.iplot.auimgr.Update()

class PlotBook(wx.aui.AuiNotebook):
    def __init__(self, parent, id = -1,size = wx.DefaultSize):
        wx.aui.AuiNotebook.__init__(self,parent,id=id,size=size)
        
        self.iplot = parent
        
        self._states = dict()
        
        bind(self,wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE,self._onPageClose)
        bind(self,wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED,self._onPageChanged)
        bind(self,wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING,self._onPageChanging)
        DEBUG_MSG("bind()", 1, self)
        
        

    def add(self,num,fig,name="Figure "):
        page = FigureFrameIPlot(self,num, fig,self.iplot)
        
        cw,ch = fig.canvas.GetClientSize()
        #l,b,w,h = fig.bbox.bounds
        print "Canvas size: ",cw,',',ch
        
        #self.iplot.toolbar.set_state(page.canvas)
        #self._states[page] =self.iplot.toolbar.get_state()
        
        #tw,th = self.iplot.toolbar.GetClientSize()
        bw,bh = self.iplot.statbar.GetSize()
        
        _W = cw
        
        _H = ch + bh
        
        self.iplot.SetClientSize(wx.Size(_W,_H))
        #self.iplot.SetSize(self.iplot.GetBestSize())
        #print self.iplot.GetSize()
        
        self.AddPage(page,caption=name + " %d" % num,select=True)   
        #page.canvas.draw()
        #_lastpage = self.GetPageCount()
        #print self.GetSelection()
       
        #i = self.GetPageIndex(page)
        #self.SetSelection(i)
        
        #page.Refresh()
        #page.Update()
       
        
        #print 'th: ',th
       
        #need to set frame size because it dictates interior sizes
        
        #self.SetInitialSize(wx.Size(fig.bbox.width, fig.bbox.height))
        #self.iplot.SetClientSize(wx.Size(_W,_H))
        
       
        #self.iplot.FitInside()
        
        #auimgr = self.iplot.GetAuiManager()
        #auimgr.Update()
        
        #self.iplot.GetSizer().Fit(self.iplot)

        #print self.iplot.GetSize()
       
        return page
   
    def _get_selected_page(self):
        num = self.GetSelection()
        
        if num < 0: return None
        
        return self.GetPage(num)

   
    def _onPageClose(self, evt):
        DEBUG_MSG("_onPageClose()", 1, self)
        num = self.GetSelection()
        #print num
        page = self.GetPage(num)
        self._states.pop(page,False)
        page.canvas.stop_event_loop()
        Gcf.destroy(page.num)
        
        
        
    def _onPageChanging(self,evt):
        DEBUG_MSG("_onPageChanging()", 1, self)
        evt.Skip()
        page = self._get_selected_page()
        if page is not None:
            self._states[page] =self.iplot.toolbar.get_state()
        
        

    def _onPageChanged(self,evt):
        DEBUG_MSG("_onPageChanged()", 1, self)
        num = self.GetSelection()
        page = self.GetPage(num)
        #iplot = page.iplot
        #iplot.SetToolBar(page.toolbar)
        #page.canvas.draw()

        Gcf.set_active(page.figmgr)
        #print self._states.keys()
        
        tb = self.iplot.toolbar
        if self._states.has_key(page):
            state = self._states[page]
            #print 'state', state
            tb.set_state(page.canvas,state)
        else:
            #print page.num
            tb.set_state(page.canvas)
            state = tb.get_state()
            self._states[page] = state
        
        tb.ToggleTool(tb._TB_ZOOM, True if state['_active'] is 'ZOOM' else False)
        tb.ToggleTool(tb._TB_PAN, True if state['_active'] is 'PAN' else False)
        tb.ToggleTool(tb._TB_MASK, True if state['_active'] is 'MASK' else False)
        tb.ToggleTool(tb._TB_UNMASK, True if state['_active'] is 'UNMASK' else False)

        
    def OnClose(self,evt):
        for i in range(0,self.GetPageCount()):
            page = self.GetPage(i)
            page.canvas.stop_event_loop()
            Gcf.destroy(page.num)
        
        #self.canvas.stop_event_loop()
        #Gcf.destroy(self.num)
   
   
   
    

########################################################################
#
# The following functions and classes are for pylab compatibility
# mode (matplotlib.pylab) and implement figure managers, etc...
#
########################################################################

def _create_wx_app():
    """
    Creates a wx.PySimpleApp instance if a wx.App has not been created.
    """
    DEBUG_MSG("_create_wx_app()", 1, None)
    wxapp = wx.GetApp()
    
    if wxapp is not None:
        pass
        #print wxapp.GetAppName()
    
    if wxapp is None:
        wxapp = wx.PySimpleApp()
        wxapp.SetExitOnFrameDelete(True)
        # retain a reference to the app object so it does not get garbage
        # collected and cause segmentation faults
        _create_wx_app.theWxApp = wxapp
        
    if getattr(wxapp,'iplot',None) is None:
        #print "New LABPLOT"
        frame = IPlotFrame(size=wx.DefaultSize) 
        wxapp.iplot = frame
    
    return wxapp

        


def new_figure_manager(num, *args, **kwargs):
    """
    Create a new figure manager instance
    """
    # in order to expose the Figure constructor to the pylab
    # interface we need to create the figure here
    DEBUG_MSG("new_figure_manager()", 3, None)
    wxapp = _create_wx_app()

    FigureClass = kwargs.pop('FigureClass', Figure)
    fig = FigureClass(*args, **kwargs)
    #fig.number = num-1
    l,b,w,h = fig.bbox.bounds
    
    iplot = wxapp.iplot
    #frame.SetPosition(wx.Point(300,100))
    plotbook = iplot.plotbook
    page = plotbook.add(num,fig)
    
    figmgr = page.get_figure_manager()
    figmgr.iplot = iplot
    
    if matplotlib.is_interactive():
        #figmgr.frame.Show()
        #figmgr.iplot.Fit()
        figmgr.iplot.Show()
    return figmgr

Toolbar = IPlotToolbar
matplotlib.backends.__all__.append('Toolbar')
FigureManager = IPlotFigureManager

def show():
    """
    Current implementation assumes that matplotlib is executed in a PyCrust
    shell. It appears to be possible to execute wxPython applications from
    within a PyCrust without having to ensure that wxPython has been created
    in a secondary thread (e.g. SciPy gui_thread).

    Unfortunately, gui_thread seems to introduce a number of further
    dependencies on SciPy modules, which I do not wish to introduce
    into the backend at this point. If there is a need I will look
    into this in a later release.
    """
    DEBUG_MSG("show()", 3, None)

    #for figwin in Gcf.get_all_fig_managers():
        #figwin.frame.Show()
        #figwin.iplot.Show()
    #DEBUG_MSG("figwin.frame.Show()", 1, None)
    
    if show._needmain and not matplotlib.is_interactive():
        DEBUG_MSG("wx.GetApp()", 1, None)
        # start the wxPython gui event if there is not already one running
        #wxapp = wx.GetApp()
        wxapp = _create_wx_app()
        #wxapp.iplot.Show()
        #wxapp.iplot.auimgr.Update()
        DEBUG_MSG("wxapp.iplot.Show()", 1, None)
        
        if wxapp is not None:
            # wxPython 2.4 has no wx.App.IsMainLoopRunning() method
            imlr = getattr(wxapp, 'IsMainLoopRunning', lambda: False)
            if not imlr():
                wxapp.MainLoop()
        show._needmain = False
    else:
        wxapp = wx.GetApp()
        wxapp.iplot.Show()
            
    DEBUG_MSG("show() end", 1, None)
show._needmain = True

#all_backends.append('WXLabplot')
#matplotlib.rcsetup._validate_standard_backends = ValidateInStrings('backend', all_backends, ignorecase=True)
#:rcParams['backend'] = 'WXLabplot'
